﻿using System.Linq;

namespace Borg.Framework
{
    public class ReplacePlug
    {
        private AutoReleaseMode _autoRelease;

        public bool Global;

        private bool _slotDefinedAutoRelease;

        public SlotInfo slotInfo;

        public ReplacePlug()
        {
            _autoRelease = AutoReleaseMode.SlotDefined;
        }

        public ReplacePlug(SlotInfo slotInfo)
        {
            _autoRelease = AutoReleaseMode.SlotDefined;
            Register(slotInfo);
        }

        public ReplacePlug(SlotInfo slotInfo, bool global)
            : this(slotInfo)
        {
            Global = global;
        }

        private void Deregistering(object sender, CancelRegisterEventArgs args)
        {
            var eti = args.PlugTypeInfo.ExtensionTypeInfo;
            
            for (var i = eti.ExtensionInfos.Count - 1; i >= 0; i--)
            {
                var ei = eti.ExtensionInfos[i];

                if (ei.Owner != slotInfo)
                    continue;

                var cnt = ei.PlugInfos.Sum(pi => pi.PluggedInSlots.Count);

                if (cnt == 1)
                    ei.Release();
            }
        }

        public void Plugging(object sender, CancelPlugEventArgs args)
        {
            if (Global)
            {
                var si = Runtime.Repository.SlotInfos.Find(slotInfo.Name);

                foreach (var slot in si)
                    foreach (var pi in slot.PluggedPlugInfos.Where(pi => pi != args.PlugInfo))
                        slot.Unplug(pi);

                args.PlugInfo.Plug();
            }
            else
                foreach (var pi in slotInfo.PluggedPlugInfos.Where(pi => pi != args.PlugInfo))
                    slotInfo.Unplug(pi);
        }

        private void Register(SlotInfo si)
        {
            if (si == null)
                return;

            slotInfo = si;
            _slotDefinedAutoRelease = si.AutoRelease;
            si.Plugging += Plugging;
            si.Deregistering += Deregistering;
        }

        private void Unregister(SlotInfo si)
        {
            if (si == null)
                return;

            si.Plugging -= Plugging;
            si.Deregistering -= Deregistering;
            si.AutoRelease = _slotDefinedAutoRelease;
        }

        public AutoReleaseMode AutoRelease
        {
            get { return _autoRelease; }
            set
            {
                _autoRelease = value;

                switch (value)
                {
                    case AutoReleaseMode.Off:
                        slotInfo.AutoRelease = false;
                        break;

                    case AutoReleaseMode.On:
                        slotInfo.AutoRelease = true;
                        break;

                    case AutoReleaseMode.SlotDefined:
                        slotInfo.AutoRelease = _slotDefinedAutoRelease;
                        break;
                }
            }
        }

        public SlotInfo SlotInfo
        {
            get { return slotInfo; }
            set
            {
                if (value == slotInfo)
                    return;

                Unregister(slotInfo);
                Register(value);
            }
        }
    }
}

